package lib

import (
	"context"
	"crypto/rand"
	"errors"
	"io"

	"server-transport-go-usage/lib/frame"
	"server-transport-go-usage/lib/message"
	. "server-transport-go-usage/lib/utils"
)

const (
	writeBufferSize = 128 //TODO: need check on biz.
)

func randomByte() (byte, error) {
	var buf [1]byte
	_, err := rand.Read(buf[:])
	return buf[0], err
}

// Channel is a communication channel created by an Endpoint.
// An Endpoint can create channels.
// For instance, a TCP client endpoint creates a single channel, while a TCP
// server endpoint creates a channel for each incoming connection.
type Channel struct {
	n     *Node
	e     Endpoint
	label string
	rwc   io.Closer // 新连接

	ctx       context.Context
	ctxCancel func()
	frw       *frame.ReadWriter //在新连接上 做读写，封装了 scanner 读协议能力
	running   bool

	// in
	chWrite chan interface{}

	// out
	done chan struct{}
}

func newDataChannel()(*Channel, error) {
	return nil, nil
}

// 一个连接包括：基于该连接的读写接口，所有消息的编解码接口实现
func newChannel(
	n *Node,
	e Endpoint,
	label string,
	rwc io.ReadWriteCloser, // 一个新的连接
) (*Channel, error) {
	//每个连接都分配一个读写器；读用于接收和解析协议；写用于编码协议和发送
	frw, err := frame.NewReadWriter(frame.ReadWriterConf{
		ReadWriter: rwc,
		DialectRW:  n.dialectRW,
	})
	if err != nil {
		return nil, err
	}

	ctx, ctxCancel := context.WithCancel(context.Background())

	return &Channel{
		n:         n,
		e:         e,
		label:     label,
		rwc:       rwc,
		ctx:       ctx,
		ctxCancel: ctxCancel,
		frw:       frw,
		chWrite:   make(chan interface{}, writeBufferSize),
		done:      make(chan struct{}),
	}, nil
}

func (ch *Channel) close() {
	ch.ctxCancel()
	if !ch.running {
		ch.rwc.Close()
	}
}

func (ch *Channel) start() {
	ch.running = true
	ch.n.wg.Add(1)
	go ch.run()
}

func (ch *Channel) run() {
	defer close(ch.done)
	defer ch.n.wg.Done()

	// 连接的通道读数据协程。
	readerDone := make(chan struct{})
	go ch.runReader(readerDone)

	/// 连接的通道写数据 协程
	writerTerminate := make(chan struct{})
	writerDone := make(chan struct{})
	go ch.runWriter(writerTerminate, writerDone)

	select {
	case <-readerDone:
		ch.rwc.Close()
		LogPrintf("receive close reader signal.")

		close(writerTerminate)
		<-writerDone

	case <-ch.ctx.Done():
		close(writerTerminate)
		<-writerDone

		ch.rwc.Close()
		<-readerDone
	}

	ch.ctxCancel()

	ch.n.pushEvent(&EventChannelClose{ch})
	ch.n.closeChannel(ch)
}

// runReader 从创建的连接上 读取数据
func (ch *Channel) runReader(readerDone chan struct{}) {
	defer close(readerDone)

	// wait client here, in order to allow the writer goroutine to start
	// and allow clients to write messages before starting listening to events
	ch.n.pushEvent(&EventChannelOpen{ch})

	for {
		fr, err := ch.frw.ReadPkgV2() //ReadPkg()
		if err != nil {
			var errRead message.ReadError
			if errors.As(err, &errRead) {
				LogPrintf("read fail, err: %v", errRead)
				ch.n.pushEvent(&EventParseError{err, ch})
				continue
			}

			var emptyErr message.EmptyPkgError
			if errors.As(err, &emptyErr) {
				LogPrintf("receive empty pkg  error. %v", err)
				continue
			}

			var ioTmout message.IoTimeoutError
			if errors.As(err, &ioTmout) {
				 continue
				//如果节点直接存在心跳数据，对数据超时是0容忍的， 那么就直接退出，结束本次连接。
				//否则，如果直接没有心跳数据，对空链路存在的场景，那么要继续等待数据接收，继续 continue.
			}
			LogPrintf("receive msg fail, err: %v", err)
			return
		}

		evt := &EventFrame{Frame: fr, Channel: ch}
		LogPrintf("send notify frame: %v", fr)
		ch.n.pushEvent(evt)
	}
}

// 每个连接的独立写 线程
func (ch *Channel) runWriter(writerTerminate chan struct{}, writerDone chan struct{}) {
	defer close(writerDone)

	for {
		select {
		case what := <-ch.chWrite:
			switch wh := what.(type) {
			case frame.MsgFrame:
				LogPrintln("send msg. ")
				err := ch.frw.WriteMsgFrame(wh) //nolint:errcheck
				if err != nil {
					LogPrintln("write msg frame fail, err: ", err)
				}
			}

		case <-writerTerminate:
			return
		}
	}
}

// String implements fmt.Stringer.
func (ch *Channel) String() string {
	return ch.label
}

// Endpoint returns the channel Endpoint.
func (ch *Channel) Endpoint() Endpoint {
	return ch.e
}

// 向具体指定的 channel上发送写数据（因为每个channel都一个独立的写协程在处理。）
func (ch *Channel) write(what interface{}) {
	select {
	case ch.chWrite <- what:
	case <-ch.ctx.Done():
	default: // buffer is full
	}
}
